/*
 * OAuth http functions in POSIX-C.
 *
 * Copyright 2007, 2008, 2009, 2010 Robin Gareus <robin@gareus.org>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */
#if HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#  define snprintf _snprintf
#endif
#include "liboauth.h"
#if _DEBUG
//#include <twit/ElapsedTime.h>
#endif

#define OAUTH_USER_AGENT "liboauth-agent/" VERSION


#include <curl/curl.h>
#include <sys/stat.h>

struct MemoryStruct {
  char *data;
  size_t size; //< bytes remaining (r), bytes accumulated (w)

  size_t start_size; //< only used with ..AndCall()
  void (*callback)(void*,int,size_t,size_t); //< only used with ..AndCall()
  void *callback_data; //< only used with ..AndCall()
};

static size_t
WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) 
{
  size_t realsize = size * nmemb;
  struct MemoryStruct *mem = (struct MemoryStruct *)data;

  mem->data = (char *)realloc(mem->data, mem->size + realsize + 1);
  if (mem->data) 
  {
    memcpy(&(mem->data[mem->size]), ptr, realsize);
    mem->size += realsize;
    mem->data[mem->size] = 0;
  }
  return realsize;
}



/**
 * cURL http post function.
 * the returned string (if not NULL) needs to be freed by the caller
 *
 * @param u url to retrieve
 * @param p post parameters 
 * @param customheader specify custom HTTP header (or NULL for none)
 * @return returned HTTP
 */
CURLcode oauth_curl_post(const char* u, const char* q, const char* customheader, char** ppResult, long* pOutHttpCode)
{
  CURL *curl;
  CURLcode res;
  struct curl_slist *slist=NULL;
   char errBuf[CURL_ERROR_SIZE]={0,};
  struct MemoryStruct chunk;
  chunk.data=NULL;
  chunk.size = 0;
  

  curl = curl_easy_init();
  if(!curl) return CURLE_FAILED_INIT;
  curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errBuf);
  curl_easy_setopt(curl, CURLOPT_URL, u);
  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, q);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
  if (customheader) 
  {
    slist = curl_slist_append(slist, customheader);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); 
  }
  curl_easy_setopt(curl, CURLOPT_USERAGENT, OAUTH_USER_AGENT);
#ifdef OAUTH_CURL_TIMEOUT  
  curl_easy_setopt(curl, CURLOPT_TIMEOUT, OAUTH_CURL_TIMEOUT);
  curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
#endif
  res = curl_easy_perform(curl);
  if(pOutHttpCode)
     curl_easy_getinfo(curl,CURLINFO_HTTP_CODE,pOutHttpCode);
  curl_slist_free_all(slist);
  if (res) 
  {
     *ppResult = strdup(errBuf);
     return res;
  }

  curl_easy_cleanup(curl);
  *ppResult = chunk.data;
  return res;
}

/**
 * cURL http get function.
 * the returned string (if not NULL) needs to be freed by the caller
 *
 * @param u url to retrieve
 * @param q optional query parameters 
 * @param customheader specify custom HTTP header (or NULL for none)
 * @return returned HTTP
 */
CURLcode oauth_curl_get(const char* u, const char* q, const char* customheader, char** ppResult, long* pOutHttpCode)
{
   CURL *curl;
  CURLcode res;
  struct curl_slist *slist=NULL;
  char *t1=NULL;
  char errBuf[CURL_ERROR_SIZE]={0,};
  struct MemoryStruct chunk;

  if (q) 
  {
    t1=(char*)malloc(sizeof(char)*(strlen(u)+strlen(q)+2));
    if(!t1) return CURLE_OUT_OF_MEMORY;
    strcat(t1,u); 
    strcat(t1,"?"); 
    strcat(t1,q);
  }

  chunk.data=NULL;
  chunk.size = 0;

  curl = curl_easy_init();
  if(!curl) return CURLE_FAILED_INIT;
  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
  curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errBuf);
  curl_easy_setopt(curl, CURLOPT_URL, q?t1:u);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
  if (customheader) 
  {
    slist = curl_slist_append(slist, customheader);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist); 
  }
#if 0 // TODO - support request methods..
  if (0) 
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "HEAD");
  else if (0) 
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
#endif
  curl_easy_setopt(curl, CURLOPT_USERAGENT, OAUTH_USER_AGENT);
#ifdef OAUTH_CURL_TIMEOUT  
  curl_easy_setopt(curl, CURLOPT_TIMEOUT, OAUTH_CURL_TIMEOUT);
  curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
#endif
  res = curl_easy_perform(curl);
  if(pOutHttpCode)
     curl_easy_getinfo(curl,CURLINFO_HTTP_CODE,pOutHttpCode);
  
  curl_slist_free_all(slist);
  if (q) free(t1);
  if (res) 
  {
     *ppResult = strdup(errBuf);
     return res;
  }

  curl_easy_cleanup(curl);
  *ppResult = chunk.data;
  return res;
}

CURLcode oauth_curl_get2(const char* u, char** ppResult, long* pOutHttpCode, response_callback cbFunction, void* userData)
{
	CURL *curl;
	CURLcode res;
	char errBuf[CURL_ERROR_SIZE]={0,};
	*ppResult=NULL;
	curl = curl_easy_init();
	if(!curl) return CURLE_FAILED_INIT;
	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errBuf);
	curl_easy_setopt(curl, CURLOPT_URL, u);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, userData);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cbFunction);
	curl_easy_setopt(curl, CURLOPT_USERAGENT, OAUTH_USER_AGENT);

	res = curl_easy_perform(curl);
	if(pOutHttpCode)
		curl_easy_getinfo(curl,CURLINFO_HTTP_CODE,pOutHttpCode);

	if (res) 
	{
		*ppResult = strdup(errBuf);
		return res;
	}

	curl_easy_cleanup(curl);
	return res;
}

/* vi:set ts=8 sts=2 sw=2: */
